package com.example.houselease.filter;

import com.alibaba.fastjson.JSON;
import com.example.houselease.security.LoginPrincipal;
import com.example.houselease.web.JsonResult;
import com.example.houselease.web.ServiceCode;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * 过滤器组件
 * 解析用户携带的jwt信息
 * @author xf
 * @version 0.0.1
 * */
@Component
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${house-lease.jwt.secretKey}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //SecurityContext是基于Session的
        // 所以，在Session未过期之前，即使后续的请求没有携带JWT，
        // Spring Security仍能从Session中找到`SecurityContext`中的`Authentication`对象，
        // 会视为此次访问与此前的访问是同一个客户端
        // 清除SecurityContext中的数据
        SecurityContextHolder.clearContext();

        //客户端应该将JWT保存在请求头（Request Header）中的名为Authorization的属性中
        //获取客户端传递的jwt
        String jwt = request.getHeader("Authorization");
        log.debug("尝试接收客户端携带的JWT数据，jwt：{}",jwt);

        // 判断客户端是否提交了有效的JWT
        if (!StringUtils.hasText(jwt) || jwt.length() < 113){
            filterChain.doFilter(request,response);
            log.debug("该请求：{}，被放行",request.getRequestURI());
            return;
        }

        //设置响应数据类型
        response.setContentType("application/json; charset=utf-8");
        Claims claims = null;
        try {
            //解析jwt
            claims = Jwts.parser()
                    .setSigningKey(this.secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        }catch (SignatureException e){
            String mes = "JWT验证签名错误，非法访问！";
            log.warn("解析JWT时出现SignatureException，响应消息：{}", mes);
            sendMes(response, ServiceCode.ERR_JWT_SIGNATURE,mes);
            return;
        }catch (MalformedJwtException e){
            String mes = "JWT数据格式错误，非法访问！";
            log.warn("解析JWT时出现MalformedJwtException，响应消息：{}", mes);
            sendMes(response,ServiceCode.ERR_JWT_MALFORMED,mes);
            return;
        }catch (ExpiredJwtException e){
            String mes = "JWT过期，请重新登录！";
            log.warn("解析JWT时出现ExpiredJwtException，响应消息：{}", mes);
            sendMes(response,ServiceCode.ERR_JWT_EXPIRED,mes);
            return;
        }
        Long id = claims.get("id",Long.class);
        String username = claims.get("username",String.class);
        String authoritiesJsonString = claims.get("authoritiesJsonString",String.class);
        log.debug("从JWT中解析得到的管理员ID：{}", id);
        log.debug("从JWT中解析得到的管理员用户名：{}", username);
        log.debug("从JWT中解析得到的管理员权限：{}", authoritiesJsonString);

        //转换json字符串为对象，获取权限信息
        List<SimpleGrantedAuthority> authorities = JSON.parseArray(authoritiesJsonString, SimpleGrantedAuthority.class);

        //创建用户登录认证信息
        LoginPrincipal principal = new LoginPrincipal();
        principal.setId(id);
        principal.setUsername(username);

        //创建认证对象
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                principal, null, authorities);

        // 将认证信息存入到SecurityContext中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        // 过滤器链继承向后执行，即：放行
        // 如果没有执行以下代码，表示“阻止”，即此请求的处理过程到此结束，在浏览器中将显示一片空白
        filterChain.doFilter(request, response);
    }

    void sendMes(HttpServletResponse response, ServiceCode state, String mes) throws IOException {
        JsonResult<Void> jsonResult = new JsonResult<>(state,mes);
        PrintWriter pw = response.getWriter();
        pw.println(JSON.toJSON(jsonResult));
        pw.close();
    }
}
